/*   The MIT License
 *   
 *   Carina Engine
 *   Copyright (c) 2009 2010 2011 2012 Zdravko Velinov
 *   
 *   Permission is hereby granted, free of charge, to any person obtaining a copy
 *   of this software and associated documentation files (the "Software"), to deal
 *   in the Software without restriction, including without limitation the rights
 *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *   copies of the Software, and to permit persons to whom the Software is
 *   furnished to do so, subject to the following conditions:
 *
 *   The above copyright notice and this permission notice shall be included in
 *   all copies or substantial portions of the Software.
 *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *   THE SOFTWARE.
 */

#ifndef RESOURCES_HH_
#define RESOURCES_HH_

#include "carina/file-system.hh"

#include <string>
#include <map>

#include "carina/material-manager.hh"
#include "carina/common/containers.hh"
#include "carina/common/exception.hh"
#include "carina/math.hh"
#include "carina/mesh.hh"
#include "carina/common/patterns.hh"
#include "carina/image.hh"

namespace Carina
{
class Mesh;
class Texture;

class ShaderProgram;
typedef shared_ptr<ShaderProgram> ShaderProgramPtr;
typedef shared_ptr<Texture> TexturePtr;

class MaterialManager;

class ResourceCache
{
    typedef std::unordered_map<string, Path> FileMap;
    DataVector<Path>    m_ResPaths;
    FileMap             m_ResMap;
public:
    void addResourcePath(const Path& path);
    void removeResourcePath(const Path& name);
    Path getResourcePath(size_t idx) const;
    size_t getResourcePathCount() const;

    void addResource(const Path& name);
    void removeResource(const Path& name);
    string getResource(const string& name) const;
    string getResourceRelative(const string& path) const;
};

typedef shared_ptr<ResourceCache> ResourceCachePtr;

class ResourceManager;
typedef std::function<bool (const string&)> ReloadCallback;

#ifdef CE_RELOADABLE_RESOURCES
#   define CE_RELOAD_CALLBACK(callback) ,callback
#else
#   define CE_RELOAD_CALLBACK(callback)
#endif

class ResourceManager
{
    RendererPtr         m_Renderer;
   
    typedef std::unordered_map<string, MeshPtr> MeshCache;
    typedef std::unordered_map<string, TexturePtr> TextureCache;
    MeshCache           m_MeshCache;
    TextureCache        m_TexCache;

    typedef std::unordered_map<string, MeshLoaderPtr> LoadersMap;
    LoadersMap          m_MeshLoaders;

    ResourceCachePtr    m_ResourceCache;

    typedef unique_ptr<MaterialManager, Deleter<MaterialManager>> MaterialManagerPtr;
    MaterialManagerPtr  m_MaterialManager;
    ImageManager        m_ImageManager;
    
#ifdef CE_RELOADABLE_RESOURCES
    FSPollingService    m_Poller;
    FSEvents            m_Events;
    
    typedef std::unordered_map<string, ReloadCallback> ReloadCallbacks;
    ReloadCallbacks     m_ReloadCallbacks;
#endif
public:
    ResourceManager(const RendererPtr& renderer, const ResourceCachePtr& rscache);

#ifdef CE_RELOADABLE_RESOURCES
    void updateResources();
    string getResource(const string& name, ReloadCallback reload_callback); // Not const
#endif

    void getMaterial(Material& material, const string& name);
    ShaderProgramPtr getShaderProgram(const string& str);
    TexturePtr getTexture(const string& str);
    MeshPtr getMesh(const string& str);
    ImagePtr getImage(const string& str);
    string getResource(const string& name) const;
    string getResourceRelative(const string& path) const;
    void clear();

    Path getResourcePath(size_t idx) const;
    size_t getResourcePathCount() const;

    RendererPtr getRenderer() const { assert(m_Renderer); return m_Renderer; }

    void registerMesh(const string& name, const MeshPtr& mesh);
    void unregisterMesh(const string& name, bool all_ref=true);
    size_t getMeshExternalRefCount(MeshPtr& mesh) const;
    void registerMeshLoader(const string& ext, const MeshLoaderPtr& mesh_loader);
    MeshLoaderPtr getMeshFileLoader(const string& filename);
};

typedef std::shared_ptr<ResourceManager> ResourceManagerPtr;
}

#endif /* RESOURCES_HH_ */
